package ro.prjmanager.web.actions;

import net.sourceforge.stripes.action.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.impl.LogFactoryImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import ro.prjmanager.core.context.SecurityContext;
import ro.prjmanager.core.util.FormModelUtil;
import ro.prjmanager.web.actions.template.TemplateAction;
import ro.prjmanager.web.annotation.*;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.sql.Connection;


@SuppressWarnings("unchecked")
public abstract class GenericAction<M> implements ActionBean {
    private SecurityContext context;


    ro.prjmanager.web.annotation.Service service;
    Create create;
    Edit edit;
    View view;
    Delete delete;
    CreateTargetMethod createTargetMethod;
    ViewTargetMethod viewTargetMethod;
    DefaultTargetMethod defaultTargetMethod;
    NomenclatorTarget nomenclatorTarget;
    private String id;
    private String oper;
    private String targetUrl;

    public static final String ADD="add";
    public static final String EDIT="edit";
    public static final String DELETE="del";


    protected GenericAction() {
        service = this.getClass().getAnnotation(ro.prjmanager.web.annotation.Service.class);
        create = this.getClass().getAnnotation(Create.class);
        edit = this.getClass().getAnnotation(Edit.class);
        view = this.getClass().getAnnotation(View.class);
        delete = this.getClass().getAnnotation(Delete.class);
        createTargetMethod = this.getClass().getAnnotation(CreateTargetMethod.class);
        viewTargetMethod = this.getClass().getAnnotation(ViewTargetMethod.class);
        defaultTargetMethod=this.getClass().getAnnotation(DefaultTargetMethod.class);
        nomenclatorTarget=this.getClass().getAnnotation(NomenclatorTarget.class);

    }

    protected final static Log log = LogFactoryImpl.getLog(TemplateAction.class);


    public void setContext(ActionBeanContext actionBeanContext) {
        this.context = (SecurityContext) actionBeanContext;
    }

    public SecurityContext getContext() {
        return this.context;
    }

    public ro.prjmanager.core.service.Service<M, Integer> getService() {
        ro.prjmanager.core.service.Service<M, Integer> ret = null;
        if (service != null)
            ret = (ro.prjmanager.core.service.Service<M, Integer>) getSpringContext().getBean(service.target());

        return ret;
    }

    public ro.prjmanager.core.service.Service<M, Integer> getService(String serviceName) {
        return  (ro.prjmanager.core.service.Service<M, Integer>) getSpringContext().getBean(serviceName);
    }




    public void create(M entity) {
        getService().create(entity);
    }

    public M load(Integer id) {
        return getService().load(id);
    }


    @HandlesEvent("cancel")
    public Resolution cancel() {
        return new RedirectResolution("/index.jsp");
    }


    public ApplicationContext getSpringContext() {
        return WebApplicationContextUtils.getRequiredWebApplicationContext(context.getServletContext());
    }

    public Connection getDBConnection() {

        return null;
    }

    @HandlesEvent("create")
    @DefaultHandler
    public Resolution create() {
        if (create == null&&defaultTargetMethod==null&&nomenclatorTarget==null)
            return null;
        Resolution ret;
        String reosurce=nomenclatorTarget!=null?nomenclatorTarget.resource():create!=null?create.resource():defaultTargetMethod.resource();
        if (create==null||create.redirect())
            ret = new RedirectResolution(reosurce);
        else  ret = new ForwardResolution(reosurce);

        if (createTargetMethod != null||defaultTargetMethod!=null||nomenclatorTarget!=null) {
            String onFailReturn=nomenclatorTarget!=null?nomenclatorTarget.onFaildReturn():createTargetMethod!=null?createTargetMethod.onFaildReturn():defaultTargetMethod.onFaildReturn();
            String targetMethod=
                    ADD.equals(getOper())&&nomenclatorTarget!=null?nomenclatorTarget.addTarget():
                    EDIT.equals(getOper())&&nomenclatorTarget!=null?nomenclatorTarget.editTarget():
                    DELETE.equals(getOper())&&nomenclatorTarget!=null?nomenclatorTarget.deleteTarget():
                    createTargetMethod!=null?createTargetMethod.target():defaultTargetMethod.target();
            try {

                Method meth = getClass().getMethod(targetMethod);
                meth.invoke(this);
            } catch (NoSuchMethodException e) {
                log.error(e);
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                log.error(e);
                e.printStackTrace();
                if (create==null||create.redirect())
                    ret = new RedirectResolution(onFailReturn);
                else ret = new ForwardResolution(createTargetMethod.onFaildReturn());

                return ret;
            } catch (IllegalAccessException e) {
                log.error(e);
                e.printStackTrace();
            }
        }

        if (targetUrl != null) {
            ret = new RedirectResolution(targetUrl);
        }

        return ret;
    }

    @HandlesEvent("view")
    public Resolution view() {
        if (view == null)
            return null;
        Resolution ret;
        if (view.redirect())
            ret = new RedirectResolution(view.resource());
        else ret = new ForwardResolution(view.resource());
        if (viewTargetMethod != null) {
            try {
                Method meth = getClass().getMethod(viewTargetMethod.target());
                meth.invoke(this);
            } catch (NoSuchMethodException e) {
                log.error(e);
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                log.error(e);
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                log.error(e);
                e.printStackTrace();
            }
        }
        if (targetUrl != null) {
            ret = new RedirectResolution(targetUrl);
        }
        return ret;
    }


    public String getTargetUrl() {
        return targetUrl;
    }

    public void setTargetUrl(String targetUrl) {
        this.targetUrl = targetUrl;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getOper() {
        return oper;
    }

    public void setOper(String oper) {
        this.oper = oper;
    }

    public void del() {
        getService().delete(Integer.valueOf(getId()));
    }


    public M getModel(){
       Class c= (Class) ((ParameterizedType) getClass()
                .getGenericSuperclass()).getActualTypeArguments()[0];
        try {
            FormModelUtil fmu=
                     new FormModelUtil(this,c.newInstance(),getContext());
            return (M) fmu.getModel();
        } catch (InstantiationException e) {
            log.error(e);
        } catch (IllegalAccessException e) {
            log.error(e);
        }
        return null;
    }

    public void injectFromModel(){
       Class c= (Class) ((ParameterizedType) getClass()
                .getGenericSuperclass()).getActualTypeArguments()[0];
        try {
            FormModelUtil fmu=
                     new FormModelUtil(this,c.newInstance(),getContext());
            fmu.getForm();
        } catch (InstantiationException e) {
            log.error(e);
        } catch (IllegalAccessException e) {
            log.error(e);
        }

    }
    
}
